﻿//
// Author: rgy
// Date: 2016-04-25 20:23:00
//

using UnityEngine;
using System.Text;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Collections;
using UnityEngine.EventSystems;
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using UnityEngine.Events;

public class SoarDText : Text
{
    private string m_text;
    private int m_fontSize;
    public bool m_supportEmoji;
    private TaskManager.TaskState m_taskState;
    public bool m_PointerTween;
    public Vector2 m_TweenScale = new Vector2(1.2f, 1.2f);

    [SerializeField]
    private bool m_LtrBestFit;
    [SerializeField]
    private int m_MinSize = 16;

    [SerializeField]
    private HrefClickEvent m_OnSetText = new HrefClickEvent();

    public HrefClickEvent onSetText
    {
        get { return m_OnSetText; }
        set { m_OnSetText = value; }
    }

    private string m_tempStr;

    //自定义emoji，范围0xE000-0xF8FF
    private static Dictionary<string, string> customEmoji = new Dictionary<string, string>
    {
        { "<<diamond>>",GetConvertedString("e000")},
        { "<<gold>>",GetConvertedString("e001")},
        { "<<coin>>",GetConvertedString("e002")},
        { "<<food>>",GetConvertedString("e003")},
    };

    public Mask m_Mask;    // 缓存mask组件，如果后面有动态添加，记得set一下

    public Mask CachedMask
    {
        get
        {
            return m_Mask;
        }
        set
        {
            m_Mask = value;
        }
    }

    public void SupportEmoji(bool isSupport)
    {
        m_supportEmoji = isSupport;
    }

    public override string text
    {
        get
        {
            return base.text;
        }
        set
        {
            if (m_taskState != null)
            {
                m_taskState.Stop();
                m_taskState = null;
            }
            m_text = value ?? "";
            m_tempStr = null;
            m_fontSize = fontSize;
            string result = GetTempText(m_text);
            if (m_supportEmoji)
            {
                if (LoadManager.Instance != null)
                {
                    result = ParseCustomEmoji(result);
                    bool hasEmoji = false;
                    base.text = EmojiParse(result, ref hasEmoji);
                    if (!hasEmoji)
                    {
                        onSetText.Invoke(text);
                    }
                }
                else
                {
                    base.text = result;
                    onSetText.Invoke(text);
                }
            }
            else
            {
                base.text = result;
                onSetText.Invoke(text);
            }
            //ResizeFontSize();
        }
    }

    private string GetTempText(string str)
    {
        return string.IsNullOrEmpty(m_tempStr) ? str : m_tempStr;
    }

    public void SetTempText(string str)
    {
        m_tempStr = str;
    }

    // 解析有点耗，缓存解析的结果，如果在缓存，直接赋值文本
    public void SetText(string str)
    {
        base.text = str;
    }

    private static string EmptyText = "                                                                 ";
    // 延迟设置文本，先设置足够长度的空文本，延迟1帧，在设置实际的文本（解决宽度自适应的情况下，阿语被强制换行的bug）
    public void SetTextDelay(string content)
    {
        base.text = EmptyText;
        TaskManager.CreateTask(DelaySetText(content)).Start();
    }

    public void SetTextBase(string txt)
    {
        m_text = txt;
        base.text = txt;
    }

    IEnumerator DelaySetText(string content)
    {
        yield return 0;
        text = content;
    }

    private ContentSizeFitter m_Fitter;
    private LayoutElement m_Layout;
    private bool m_InitComponent = false;

    private void InitComponent()
    {
        if (m_InitComponent) return;
        m_Fitter = gameObject.GetComponent<ContentSizeFitter>();
        m_Layout = gameObject.GetComponent<LayoutElement>();
        m_InitComponent = true;
    }

    public float width
    {
        get
        {
            InitComponent();
            if (m_Fitter && m_Fitter.horizontalFit == ContentSizeFitter.FitMode.PreferredSize)
            {
                return 640;
            }
            float len = rectTransform.rect.width;
            if (len > 0) return len;
            if (m_Layout && m_Layout.preferredWidth > 0)
            {
                return m_Layout.preferredWidth;
            }
            HorizontalLayoutGroup horizontal = transform.parent.GetComponent<HorizontalLayoutGroup>();
            if (horizontal)
            {
                return 0;
            }
            VerticalLayoutGroup verticalGroup = transform.parent.GetComponent<VerticalLayoutGroup>();
            len = (transform.parent.transform as RectTransform).rect.width;
            if (verticalGroup)
            {
                if (len <= 0)
                {
                    len = 640;
                }
                return len - verticalGroup.padding.left - verticalGroup.padding.right;
            }
            return len;
        }
    }

    public string FormatText(string label)
    {
        float txtWidth = width;
        if (txtWidth <= 0)
        {
            txtWidth = 640;
        }
        txtWidth -= 1;
        int textLength = label.Length;
        StringBuilder sb = new StringBuilder();
        int start = 0;
        int offset;
        float remainWidth = txtWidth;
        for (offset = 0; offset < textLength; offset++)
        {
            char ch = label[offset];
            if (ch == '\n')
            {
                string word = label.Substring(start, offset - start + 1);
                float wordLength = GetCharLength(word);
                if (remainWidth - wordLength < 0)
                {
                    if (txtWidth < wordLength)
                    {
                        if (remainWidth < txtWidth)
                        {
                            EndLine(ref sb);
                        }
                        DelWordTooLong(ref sb, word);
                    }
                    else
                    {
                        EndLine(ref sb);
                        sb.Append(word);
                    }
                }
                else
                {
                    sb.Append(word);
                }
                remainWidth = txtWidth;
                start = offset + 1;
                continue;
            }
            if (IsSpace(ch))
            {
                string word = label.Substring(start, offset - start + 1);
                float wordLength = GetCharLength(word);
                if (remainWidth - wordLength < 0)
                {
                    //有可能一个单词就叼炸天了.
                    if (txtWidth < wordLength)
                    {
                        if (remainWidth < txtWidth)
                        {
                            EndLine(ref sb);
                        }
                        DelWordTooLong(ref sb, word);
                    }
                    else
                    {
                        EndLine(ref sb);
                        sb.Append(word);
                        remainWidth = txtWidth - wordLength;
                    }
                }
                else
                {
                    sb.Append(word);
                    remainWidth -= wordLength;
                }
                start = offset + 1;
            }
        }
        if (start < offset)
        {
            string word = label.Substring(start, offset - start);
            float wordLength = GetCharLength(word);
            if (remainWidth - wordLength < 0)
            {
                if (txtWidth < wordLength)
                {
                    if (remainWidth < txtWidth)
                    {
                        EndLine(ref sb);
                    }
                    DelWordTooLong(ref sb, word);
                }
                else
                {
                    EndLine(ref sb);
                    sb.Append(word);
                }
            }
            else
            {
                sb.Append(word);
            }
        }
        return sb.ToString();
    }

    public void SetBestFit(int minSize)
    {
        m_LtrBestFit = true;
        resizeTextMinSize = minSize;
        resizeTextMaxSize = fontSize;
    }

    public void DelWordTooLong(ref StringBuilder sb, string str)
    {
        StringBuilder temp = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            temp.Append(str[i]);
            float tempLength = GetCharLength(temp.ToString());
            if (tempLength > width)
            {
                temp.Length = temp.Length - 1;
                sb.Append(temp).Append("\n");
                temp.Length = 0;
                temp.Append(str[i]);
            }
        }
        if (temp.Length > 0)
        {
            sb.Append(temp);
        }
    }

    public float GetCharLength(string ch)
    {
        int w = 0;
        try
        {
            CharacterInfo characterInfo;
            font.RequestCharactersInTexture(ch, m_fontSize, FontStyle.Normal);
            for (int i = 0; i < ch.Length; i++)
            {
                font.GetCharacterInfo(ch[i], out characterInfo, m_fontSize);
                w += characterInfo.advance;
            }
        }
        catch (Exception e)
        {
            Debuger.LogError(e.Message);
        }
        return w;
    }

    public static bool IsSpace(int ch)
    {
        return (ch == ' ' || ch == 0x200a || ch == 0x200b || ch == '\u2009');
    }

    public static void EndLine(ref StringBuilder s)
    {
        int i = s.Length - 1;
        if (i > 0 && IsSpace(s[i])) s[i] = '\n';
        else s.Append('\n');
    }

    public void WriteText(string msg)
    {
        base.text = msg;
    }

    protected override void Start()
    {
        if (m_PointerTween)
        {
            PointerClickListener.AddPointerDown(gameObject, OnPointerDown);
            PointerClickListener.AddPointerUp(gameObject, OnPointerUp);
        }
    }

    private void OnPointerDown(GameObject go, BaseEventData eventData)
    {
        rectTransform.localScale = m_TweenScale;
    }

    private void OnPointerUp(GameObject go, BaseEventData eventData)
    {
        rectTransform.localScale = Vector3.one;
    }

    /// <summary>
    /// 重写这个MaskableGraphic的虚函数，默认实现的getcomponent很耗
    /// </summary>
    /// <param name="baseMaterial"></param>
    /// <returns></returns>
    public override Material GetModifiedMaterial(Material baseMaterial)
    {
        var toUse = baseMaterial;

        if (m_ShouldRecalculateStencil)
        {
            var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
            m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;
            m_ShouldRecalculateStencil = false;
        }

        // if we have a Mask component then it will
        // generate the mask material. This is an optimisation
        // it adds some coupling between components though :(
        if (m_StencilValue > 0 && CachedMask == null)
        {
            var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, UnityEngine.Rendering.StencilOp.Keep, UnityEngine.Rendering.CompareFunction.Equal, UnityEngine.Rendering.ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
            StencilMaterial.Remove(m_MaskMaterial);
            m_MaskMaterial = maskMat;
            toUse = m_MaskMaterial;
        }
        return toUse;
    }

    #region EmojiParse

    private static string[] m_emojiInventory;
    private static Dictionary<string, string> emojiRects;

    private static char emSpace = '\u2001';

    private float SceenScale
    {
        get
        {
            if (Config.Instance.iPadScreen)
            {
                return 1136f / Screen.height;
            }
            else
            {
                return 640.0f / Screen.width;
            }
        }
    }

    /// <summary>
    /// 载入Emoji信息
    /// </summary>
    private void ParseEmojiInfo()
    {
        if (emojiRects != null) return;
        if (m_emojiInventory == null)
        {
            LoadManager.Instance.LoadEmoji((data) =>
            {
                m_emojiInventory = data as string[];
            }, false, true);
        }
        emojiRects = new Dictionary<string, string>();
        for (int i = 0; i < m_emojiInventory.Length; i++)
        {
            emojiRects[GetConvertedString(m_emojiInventory[i])] = m_emojiInventory[i];
        }
    }

    //转换自定义emoji
    private string ParseCustomEmoji(string inputString)
    {
        foreach (var emoji in customEmoji)
        {
            if (inputString.Contains(emoji.Key))
                inputString = inputString.Replace(emoji.Key, emoji.Value);
        }
        return inputString;
    }

    /// <summary>
    /// 解析Emoji 
    /// </summary>
    /// <param name="inputString"></param>
    /// <returns></returns>
    public string EmojiParse(string inputString, ref bool hasEmoji)
    {
        //隐藏掉所有EMOJI
        for (int j = 0; j < _emojiImages.Count; j++)
        {
            _emojiImages[j].gameObject.SetActive(false);
        }
        bool isEmoji = false;
        for (int x = 0; x < inputString.Length; x++)
        {
            if (UIHelper.isEmojiCharacter(inputString[x]))
            {
                isEmoji = true;
                break;
            }
        }
        if (!isEmoji)
        {
            onSetText.Invoke(inputString);
            return inputString;
        }
        ParseEmojiInfo();
        List<EmojiTuple> emojiReplacements = new List<EmojiTuple>();
        StringBuilder sb = new StringBuilder();
        int i = 0;
        while (i < inputString.Length)
        {
            string singleChar = inputString.Substring(i, 1);
            string doubleChar = inputString.Length - 1 <= i ? string.Empty : inputString.Substring(i, 2);
            string fourChar = inputString.Length - 3 <= i ? string.Empty : inputString.Substring(i, 4);

            if (emojiRects.ContainsKey(fourChar))
            {
                sb.Append(emSpace);
                emojiReplacements.Add(new EmojiTuple(sb.Length - 1, fourChar));
                i += 4;
            }
            else if (emojiRects.ContainsKey(doubleChar))
            {
                sb.Append(emSpace);
                emojiReplacements.Add(new EmojiTuple(sb.Length - 1, doubleChar));
                i += 2;
            }
            else if (emojiRects.ContainsKey(singleChar))
            {
                sb.Append(emSpace);
                emojiReplacements.Add(new EmojiTuple(sb.Length - 1, singleChar));
                i++;
            }
            else
            {
                if (!UIHelper.isEmojiCharacter(inputString[i]))
                {
                    sb.Append(inputString[i]);
                }
                i++;
            }
        }
        if (emojiReplacements.Count > 0)
        {
            hasEmoji = true;
            m_taskState = TaskManager.CreateTask(RefreshEmoji(emojiReplacements));
            m_taskState.Start();
        }
        else
        {
            SpawnEmojiImages(0);
            onSetText.Invoke(sb.ToString());
        }
        return sb.ToString();
    }

    /// <summary>
    /// 让Text刷完下一帧刷Emoji.. 
    /// </summary>
    /// <param name="emojiReplacements"></param>
    /// <returns></returns>
    public IEnumerator RefreshEmoji(List<EmojiTuple> emojiReplacements)
    {
        yield return null;
        try
        {
            TextGenerator textGen = cachedTextGenerator;
            if (textGen.verts.Count == 0) textGen = cachedTextGeneratorForLayout;
            //Debuger.Log("emojii count:" + emojiReplacements.Count + "  textGen.verts.Count:" + textGen.verts.Count);
            for (int j = 0; j < emojiReplacements.Count; j++)
            {
                int emojiIndex = emojiReplacements[j].pos;
                int vertsIndex = emojiIndex * 4;
                //Debug.Log(emojiIndex + " " + vertsIndex + " " + " Text:" + text);
                if (vertsIndex < textGen.verts.Count - 4)
                {
                    SpawnEmojiImages(j + 1);
                    Vector2 imagePos = new Vector2(textGen.verts[vertsIndex].position.x, textGen.verts[vertsIndex].position.y);
                    imagePos = imagePos * SceenScale;
                    SpriteInfo spriteInfo = LoadManager.Instance.GetEmoji(emojiRects[emojiReplacements[j].emoji]);
                    _emojiImages[j].SetSpriteInfo(spriteInfo);
                    _emojiImages[j].rectTransform.localPosition = imagePos;
                }
            }
            onSetText.Invoke(text);
        }
        catch (Exception e)
        {
            // ignored
        }
    }

    public struct EmojiTuple
    {
        public int pos;
        public string emoji;

        public EmojiTuple(int p, string s)
        {
            pos = p;
            emoji = s;
        }
    }

    private static string GetConvertedString(string inputString)
    {
        string[] converted = inputString.Split('-');
        for (int j = 0; j < converted.Length; j++)
        {
            string str = converted[j];
            if (str.IndexOf('_') == -1)
                converted[j] = char.ConvertFromUtf32(Convert.ToInt32(str, 16));
        }
        return string.Join(string.Empty, converted);
    }

    //emoji缓存池
    private List<SDImage> _emojiImages = new List<SDImage>();
    private List<SDImage> _cacheImages = new List<SDImage>();
    private void SpawnEmojiImages(int count)
    {
        try
        {

            while (_emojiImages.Count < count)
            {
                if (_cacheImages.Count > 0)
                {
                    _emojiImages.Add(_cacheImages[0]);
                    _cacheImages.RemoveAt(0);
                }
                else
                {
                    GameObject go = new GameObject { name = "emoji_face" };
                    go.transform.SetParent(transform);
                    SDImage img = go.AddComponent<SDImage>();
                    img.raycastTarget = false;
                    RectTransform rt = img.rectTransform;
                    rt.sizeDelta = new Vector2(fontSize, fontSize);
                    rt.anchorMin = new Vector2(0, 1);
                    rt.anchorMax = new Vector2(0, 1);
                    rt.pivot = new Vector2(0, 0.16f);
                    rt.localScale = Vector3.one;
                    _emojiImages.Add(img);
                }
            }
            while (_emojiImages.Count > count)
            {
                int lastIndex = _emojiImages.Count - 1;
                _emojiImages[lastIndex].gameObject.SetActive(false);
                _cacheImages.Add(_emojiImages[lastIndex]);
                _emojiImages.RemoveAt(lastIndex);
            }
            for (int i = 0; i < _emojiImages.Count; i++)
            {
                _emojiImages[i].gameObject.SetActive(true);
                _emojiImages[i].rectTransform.sizeDelta = new Vector2(fontSize, fontSize);
            }
        }
        catch (Exception)
        {
            // ignored
        }
    }

    public void ClearEmojii()
    {
        if (_emojiImages != null)
        {
            foreach (var emoji in _emojiImages)
            {
                Destroy(emoji.gameObject);
            }
            _emojiImages.Clear();
        }
    }
    #endregion

    #region 超链接(坐标跳转)
    /// <summary>
    /// 图片池
    /// </summary>
    private readonly List<Image> m_ImagesPool = new List<Image>();

    /// <summary>
    /// 图片的最后一个顶点的索引
    /// </summary>
    private readonly List<int> m_ImagesVertexIndex = new List<int>();

    public bool supportHyperLink = false;
    private bool supportLine = false;
    private Color colorLine;
    private const string strLine = "______________________________";

    /// <summary>
    /// 点击事件检测是否点击到超链接文本
    /// </summary>
    public void OnPointerClick()
    {
        Vector2 lp;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            rectTransform, Input.mousePosition, Config.Instance.UICamera, out lp);

        foreach (var hrefInfo in m_HrefInfos)
        {
            var boxes = hrefInfo.boxes;
            for (var i = 0; i < boxes.Count; ++i)
            {
                if (boxes[i].Contains(lp))
                {
                    m_OnHrefClick.Invoke(hrefInfo.name);
                    return;
                }
            }
        }

        m_OnHrefClick.Invoke(string.Empty);
    }

    public override float preferredWidth
    {
        get
        {
            if (supportHyperLink)
            {
                var settings = GetGenerationSettings(Vector2.zero);
                return cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit;
            }
            else
            {
                float ret = 0;
                try
                {
                    ret = base.preferredWidth;
                }
                catch (Exception e)
                {
                    Debuger.LogError(e.Message);
                }
                return ret;
            }
        }
    }

    public override float preferredHeight
    {
        get
        {
            if (supportHyperLink)
            {
                var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f));
                return cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit;
            }
            else
            {
                float ret = 0;
                try
                {
                    ret = base.preferredHeight;
                }
                catch (Exception e)
                {
                    Debuger.LogError(e.Message);
                }
                return ret;
            }
        }
    }

    /// <summary>
    /// 正则取出所需要的属性
    /// </summary>
    private static readonly Regex s_Regex =
        new Regex(@"<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />", RegexOptions.Singleline);

    public override void SetVerticesDirty()
    {
        base.SetVerticesDirty();

        if (supportHyperLink)
            UpdateQuadImage();
    }

    /// <summary>
    /// 解析完最终的文本
    /// </summary>
    private string m_OutputText;

    protected void UpdateQuadImage()
    {
#if UNITY_EDITOR
        if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
        {
            return;
        }
#endif
        m_OutputText = GetOutputText();
        m_ImagesVertexIndex.Clear();
        foreach (Match match in s_Regex.Matches(m_OutputText))
        {
            var picIndex = match.Index;
            var endIndex = picIndex * 4 + 3;
            m_ImagesVertexIndex.Add(endIndex);

            m_ImagesPool.RemoveAll(image => image == null);
            if (m_ImagesPool.Count == 0)
            {
                GetComponentsInChildren(m_ImagesPool);
            }
            if (m_ImagesVertexIndex.Count > m_ImagesPool.Count)
            {
                var resources = new DefaultControls.Resources();
                var go = DefaultControls.CreateImage(resources);
                go.layer = gameObject.layer;
                var rt = go.transform as RectTransform;
                if (rt)
                {
                    rt.SetParent(rectTransform);
                    rt.localPosition = Vector3.zero;
                    rt.localRotation = Quaternion.identity;
                    rt.localScale = Vector3.one;
                }
                m_ImagesPool.Add(go.GetComponent<Image>());
            }

            var spriteName = match.Groups[1].Value;
            var size = float.Parse(match.Groups[2].Value);
            var img = m_ImagesPool[m_ImagesVertexIndex.Count - 1];
            if (img.sprite == null || img.sprite.name != spriteName)
            {
                img.sprite = Resources.Load<Sprite>(spriteName);
            }
            img.rectTransform.sizeDelta = new Vector2(size, size);
            img.enabled = true;
        }

        for (var i = m_ImagesVertexIndex.Count; i < m_ImagesPool.Count; i++)
        {
            if (m_ImagesPool[i])
            {
                m_ImagesPool[i].enabled = false;
            }
        }
    }

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        _hasInitPopulateMesh = true;
        if (!supportHyperLink)
        {
            base.OnPopulateMesh(toFill);
            return;
        }

        var orignText = m_Text;
        m_Text = m_OutputText;
        base.OnPopulateMesh(toFill);
        m_Text = orignText;

        UIVertex vert = new UIVertex();
        for (var i = 0; i < m_ImagesVertexIndex.Count; i++)
        {
            var endIndex = m_ImagesVertexIndex[i];
            var rt = m_ImagesPool[i].rectTransform;
            var size = rt.sizeDelta;
            if (endIndex < toFill.currentVertCount)
            {
                toFill.PopulateUIVertex(ref vert, endIndex);
                rt.anchoredPosition = new Vector2(vert.position.x + size.x / 2, vert.position.y + size.y / 2);

                // 抹掉左下角的小黑点
                toFill.PopulateUIVertex(ref vert, endIndex - 3);
                var pos = vert.position;
                for (int j = endIndex, m = endIndex - 3; j > m; j--)
                {
                    toFill.PopulateUIVertex(ref vert, endIndex);
                    vert.position = pos;
                    toFill.SetUIVertex(vert, j);
                }
            }
        }

        if (m_ImagesVertexIndex.Count != 0)
        {
            m_ImagesVertexIndex.Clear();
        }

        // 处理超链接包围框
        foreach (var hrefInfo in m_HrefInfos)
        {
            hrefInfo.boxes.Clear();
            if (hrefInfo.startIndex >= toFill.currentVertCount)
            {
                continue;
            }

            // 将超链接里面的文本顶点索引坐标加入到包围框
            toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);
            var pos = vert.position;
            var bounds = new Bounds(pos, Vector3.zero);
            for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++)
            {
                if (i >= toFill.currentVertCount)
                {
                    break;
                }

                toFill.PopulateUIVertex(ref vert, i);
                pos = vert.position;
                if (pos.x < bounds.min.x) // 换行重新添加包围框
                {
                    hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
                    bounds = new Bounds(pos, Vector3.zero);
                }
                else
                {
                    bounds.Encapsulate(pos); // 扩展包围框
                }
            }
            hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
        }

        //添加下划线
        StartCoroutine(AddLinkLine());
    }

    private IEnumerator AddLinkLine()
    {
        yield return 0;
        if (supportLine)
        {
            for (int i = m_HrefInfos.Count - 1; i >= 0; i--)
            {
                var hrefInfo = m_HrefInfos[i];
                for (int j = hrefInfo.boxes.Count - 1; j >= 0; j--)
                {
                    GameObject line = new GameObject("line");
                    line.transform.parent = transform;
                    line.transform.localScale = Vector3.one;

                    SoarDText text = line.AddComponent<SoarDText>();
                    text.font = font;
                    text.fontSize = fontSize - 2;
                    text.text = strLine;
                    text.alignment = TextAnchor.LowerCenter;

                    text.raycastTarget = false;
                    text.color = colorLine;

                    var box = hrefInfo.boxes[j];
                    RectTransform rect = line.GetComponent<RectTransform>();
                    rect.pivot = Vector2.zero;
                    rect.anchorMin = new Vector2(0.5f, 1f);
                    rect.anchorMax = new Vector2(0.5f, 1f);
                    rect.anchoredPosition = box.position;
                    rect.sizeDelta = box.size;
                    rect.SetAnchor(TextAnchor.UpperLeft);
                    rect.localPosition = new Vector3(rect.localPosition.x, rect.localPosition.y, 0);
                    hrefInfo.lines.Add(text);
                }
            }
        }
    }

    /// <summary>
    /// 超链接信息列表
    /// </summary>
    private readonly List<HrefInfo> m_HrefInfos = new List<HrefInfo>();

    /// <summary>
    /// 文本构造器
    /// </summary>
    private static readonly StringBuilder s_TextBuilder = new StringBuilder();

    /// <summary>
    /// 超链接正则
    /// </summary>
    private static readonly Regex s_HrefRegex =
        new Regex(@"<a href=([^>\n\s]+) color=(.*?)>(.*?)(</a>)", RegexOptions.Singleline);

    [Serializable]
    public class HrefClickEvent : UnityEvent<string> { }

    [SerializeField]
    private HrefClickEvent m_OnHrefClick = new HrefClickEvent();

    /// <summary>
    /// 超链接点击事件
    /// </summary>
    public HrefClickEvent onHrefClick
    {
        get { return m_OnHrefClick; }
        set { m_OnHrefClick = value; }
    }

    /// <summary>
    /// 获取超链接解析后的最后输出文本
    /// </summary>
    /// <returns></returns>
    protected string GetOutputText()
    {
        s_TextBuilder.Length = 0;

        //清空超链接数据
        foreach (var hrefInfo in m_HrefInfos)
        {
            for (int i = hrefInfo.lines.Count - 1; i >= 0; i--)
                Destroy(hrefInfo.lines[i].gameObject, 0.1f);
            hrefInfo.lines.Clear();
        }
        m_HrefInfos.Clear();

        var indexText = 0;
        foreach (Match match in s_HrefRegex.Matches(text))
        {
            s_TextBuilder.Append(text.Substring(indexText, match.Index - indexText));
            s_TextBuilder.Append("<color=");  // 超链接颜色
            s_TextBuilder.Append(match.Groups[2].Value);
            s_TextBuilder.Append(">");

            var group = match.Groups[1];
            var hrefInfo = new HrefInfo
            {
                startIndex = s_TextBuilder.Length * 4, // 超链接里的文本起始顶点索引
                endIndex = (s_TextBuilder.Length + match.Groups[3].Length - 1) * 4 + 3,
                name = group.Value
            };
            m_HrefInfos.Add(hrefInfo);

            s_TextBuilder.Append(match.Groups[3].Value);
            s_TextBuilder.Append("</color>");
            indexText = match.Index + match.Length;
        }
        s_TextBuilder.Append(text.Substring(indexText, text.Length - indexText));
        return s_TextBuilder.ToString();
    }

    /// <summary>
    /// 超链接信息类
    /// </summary>
    private class HrefInfo
    {
        public int startIndex;

        public int endIndex;

        public string name;

        public readonly List<Rect> boxes = new List<Rect>();

        public List<SoarDText> lines = new List<SoarDText>();
    }
    #endregion

    #region 扩展接口

    /// <summary>
    /// 获取完美适配宽度
    /// </summary>
    /// <returns></returns>
    public float GetPreferredWidth()
    {
        return GetCharLength(text);
    }

    public void ClampText(string txt, string suffix)
    {
        m_text = txt;
        string result = m_text;
        if (!string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(suffix))
            base.text = UIHelper.ClampText(this, result, suffix);
    }

    public void SetColor(string txt, string color)
    {
        string text = UIHelper.SetColor(this, txt, color);
        if (text != null)
        {
            supportRichText = true;
            base.text = text;
        }
    }

    public void SetLink(string txt, string color, string herf, bool isLine = true)
    {
        string text = UIHelper.SetLink(this, txt, color, herf);
        if (text != null)
        {
            Button button = GetComponent<Button>();
            if (button == null) button = gameObject.AddComponent<Button>();
            button.transition = Selectable.Transition.None;
            button.onClick.RemoveAllListeners();
            button.onClick.AddListener(OnPointerClick);

            supportRichText = true;
            supportHyperLink = true;
            raycastTarget = true;
            supportLine = isLine;

            ColorUtility.TryParseHtmlString(color, out colorLine);
            base.text = text;
        }
    }
    #endregion

    #region 预留代码快

    //自适配字号不推荐使用!!!()
    //            if (resizeTextForBestFit)
    //            {
    //                m_maxFontSize = resizeTextMaxSize;
    //                for (int i = resizeTextMaxSize; i >= resizeTextMinSize; i--)
    //                {
    //                    base.text = m_text;
    //                    if (preferredHeight <= rectTransform.rect.height)
    //                    {
    //                        m_fontSize = i;
    //                        break;
    //                    }
    //                    resizeTextMaxSize = i;
    //                }
    //            }


    #endregion

    #region Cull显隐方式
    bool _cullNeedRebuild = false;
    bool _hasInitPopulateMesh = false;
    public bool m_Cull
    {
        get
        {
            return canvasRenderer.cull;
        }
        set
        {
            canvasRenderer.cull = value;
            if ((_cullNeedRebuild && !value) || (!_hasInitPopulateMesh && !value))
            {
                SetVerticesDirty();
                _cullNeedRebuild = false;
            }
        }
    }
    public override void Rebuild(CanvasUpdate update)
    {
        base.Rebuild(update);
        if (m_Cull)
        {
            _cullNeedRebuild = true;
        }
    }
    // protected override void OnEnable()
    // {
    //     base.OnEnable();
    //     if(m_Cull){
    //         _cullNeedRebuild = true;
    //     }
    // }
    // protected override void OnDisable()
    // {
    //     base.OnDisable();
    //     _cullNeedRebuild = false;
    // }
    #endregion
}
